﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Reflection;

namespace Luka;

/// <summary>
/// 依赖注入设置
/// </summary>
[NotScanned]
public static class DependencySetup
{
    /// <summary>
    /// 自动添加服务
    /// </summary>
    /// <returns></returns>
    public static IServiceCollection AddRegisterService(this IServiceCollection services)
    {
        var types = InternalApp.TypeList
             //IsAssignableFrom ，用于检查一个类型是否可以分配给另一个类型。IsDefined 用于检查一个类型上是否定义了指定的自定义特性
             .Where(it => typeof(IPrivateDependency).IsAssignableFrom(it) || it.HasAttribute<InjectionAttribute>())
             .Where(it => !it.Equals(typeof(IPrivateDependency)))
             .Where(it => !it.Equals(typeof(IScoped)))
             .Where(it => !it.Equals(typeof(ITransient)))
             .Where(it => !it.Equals(typeof(ISingleton)))
             .Where(it => !it.Equals(typeof(IDisposable)))
             .Where(it => !it.Equals(typeof(IAsyncDisposable)))
             .Where(it => (!it.IsAbstract && !it.IsSealed && it.IsClass) || it.IsInterface)
             .ToList();

        //所有符合条件的接口
        var interfaceTypes = types.Where(it => it.IsInterface).ToList();

        #region 继承接口注入

        foreach (var itemType in types.Where(it => it.IsClass && typeof(IPrivateDependency).IsAssignableFrom(it)))
        {
            if (typeof(IScoped).IsAssignableFrom(itemType))
            {
                services.AddScoped(interfaceTypes, itemType);
            }
            else if (typeof(ISingleton).IsAssignableFrom(itemType))
            {
                services.AddSingleton(interfaceTypes, itemType);
            }
            else
            {
                services.AddTransient(interfaceTypes, itemType);
            }
        }

        #endregion 继承接口注入

        #region 特性注入

        //过滤出所有带有InjectionAttribute特性的类，并且不是私有依赖
        foreach (var itemType in types.Where(it => it.IsClass && it.HasAttribute<InjectionAttribute>() && !typeof(IPrivateDependency).IsAssignableFrom(it)))
        {
            var attribute = itemType.GetCustomAttribute<InjectionAttribute>(false);
            if (attribute != null)
            {
                if (attribute.Type == InjectType.IScoped)
                {
                    services.AddScoped(interfaceTypes, itemType);
                }
                else if (attribute.Type == InjectType.ISingleton)
                {
                    services.AddSingleton(interfaceTypes, itemType);
                }
                else
                {
                    services.AddTransient(interfaceTypes, itemType);
                }
            }
        }

        #endregion 特性注入

        return services;
    }

    /// <summary>
    /// 作用域注入
    /// </summary>
    private static void AddScoped(this IServiceCollection services, List<Type> interfaceList, Type type)
    {
        var interfaceType = interfaceList.FirstOrDefault(x => x.IsAssignableFrom(type));
        if (interfaceType != null)
        {
            services.TryAddScoped(interfaceType, type);
        }
        else
        {
            services.TryAddScoped(type);
        }
    }

    /// <summary>
    /// 单例注入
    /// </summary>
    private static void AddSingleton(this IServiceCollection services, List<Type> interfaceList, Type type)
    {
        var interfaceType = interfaceList.FirstOrDefault(x => x.IsAssignableFrom(type));
        if (interfaceType != null)
        {
            services.TryAddSingleton(interfaceType, type);
        }
        else
        {
            services.TryAddSingleton(type);
        }
    }

    /// <summary>
    /// 瞬时注入
    /// </summary>
    private static void AddTransient(this IServiceCollection services, List<Type> interfaceList, Type type)
    {
        var interfaceType = interfaceList.FirstOrDefault(x => x.IsAssignableFrom(type));
        if (interfaceType != null)
        {
            services.TryAddTransient(interfaceType, type);
        }
        else
        {
            services.TryAddTransient(type);
        }
    }
}